import requests
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry
from requests.exceptions import RequestException
import time


class RequestUtils:
    # 连接池配置
    POOL_SIZE = 10  # 每个主机的最大连接数
    POOL_MAXSIZE = 20  # 连接池的最大大小
    POOL_BLOCK = False  # 连接池满时是否阻塞

    # 重试配置
    DEFAULT_RETRIES = 3  # 默认重试次数
    BACKOFF_FACTOR = 1.0  # 退避因子，控制重试间隔时间
    STATUS_FORCELIST = [500, 502, 503, 504, 520, 521, 522, 523, 524]  # 需要重试的状态码
    METHOD_WHITELIST = ["GET", "POST", "PUT", "DELETE"]  # 允许重试的HTTP方法

    # 超时配置
    DEFAULT_TIMEOUT = 10  # 默认超时时间（秒）

    @classmethod
    def _get_session(cls, verify=True, cert=None, timeout=DEFAULT_TIMEOUT):
        """创建并配置带有连接池和重试机制的会话"""
        session = requests.Session()

        # 创建自定义重试策略
        retry_strategy = Retry(
            total=cls.DEFAULT_RETRIES,
            backoff_factor=cls.BACKOFF_FACTOR,
            status_forcelist=cls.STATUS_FORCELIST,
            allowed_methods=cls.METHOD_WHITELIST,
            raise_on_status=False,  # 不要在达到最大重试次数后立即引发异常
        )

        # 创建支持连接池的HTTP适配器
        adapter = HTTPAdapter(
            max_retries=retry_strategy,
            pool_connections=cls.POOL_SIZE,
            pool_maxsize=cls.POOL_MAXSIZE,
            pool_block=cls.POOL_BLOCK
        )

        # 应用适配器到所有HTTP和HTTPS请求
        session.mount("http://", adapter)
        session.mount("https://", adapter)

        # 配置SSL验证和客户端证书
        session.verify = verify
        if cert:
            session.cert = cert

        # 移除错误的超时适配器，超时设置将通过会话的send方法处理

        return session

    @staticmethod
    def _handle_response(response):
        """统一处理响应结果"""
        try:
            response.raise_for_status()
        except requests.exceptions.HTTPError as e:
            error_msg = f"HTTP错误 {response.status_code}: {response.text}"
            raise RequestException(error_msg) from e
        return response

    @classmethod
    def get(cls, url, headers=None, params=None, verify=True, cert=None, timeout=DEFAULT_TIMEOUT, retry_on_exception=True):
        """
        Sends a GET request to the specified URL with SSL verification and retry mechanism.

        :param url: URL to send the GET request to.
        :param headers: Optional headers for the request.
        :param params: Optional query parameters.
        :param verify: Verify SSL certificate (default: True).
        :param cert: Path to SSL client certificate file or tuple (cert_file, key_file).
        :param timeout: Timeout in seconds (default: 10).
        :param retry_on_exception: Whether to retry on exceptions (default: True).
        :return: Response object.
        """
        last_exception = None

        for attempt in range(cls.DEFAULT_RETRIES + 1):
            if attempt > 0:
                wait_time = cls.BACKOFF_FACTOR * (2 ** (attempt - 1))
                time.sleep(wait_time)
                print(f"重试请求 ({attempt}/{cls.DEFAULT_RETRIES}): {url}")

            try:
                with cls._get_session(verify, cert, timeout) as session:
                    # 在这里设置超时参数
                    response = session.get(url, headers=headers, params=params, timeout=timeout)
                    return cls._handle_response(response)
            except RequestException as e:
                last_exception = e
                # 只在明确指定重试异常且不是最后一次尝试时继续
                if not retry_on_exception or attempt >= cls.DEFAULT_RETRIES:
                    raise

        # 如果循环结束仍未成功，抛出最后一个异常
        raise last_exception

    @classmethod
    def post(cls, url, data=None, json=None, headers=None, verify=True, cert=None, timeout=DEFAULT_TIMEOUT, retry_on_exception=True):
        """
        Sends a POST request to the specified URL with SSL verification and retry mechanism.

        :param url: URL to send the POST request to.
        :param data: Data to be sent in the body of the request (form-encoded).
        :param json: JSON data to be sent in the body of the request.
        :param headers: Optional headers for the request.
        :param verify: Verify SSL certificate (default: True).
        :param cert: Path to SSL client certificate file or tuple (cert_file, key_file).
        :param timeout: Timeout in seconds (default: 10).
        :param retry_on_exception: Whether to retry on exceptions (default: True).
        :return: Response object.
        """
        last_exception = None

        for attempt in range(cls.DEFAULT_RETRIES + 1):
            if attempt > 0:
                wait_time = cls.BACKOFF_FACTOR * (2 ** (attempt - 1))
                time.sleep(wait_time)
                print(f"重试请求 ({attempt}/{cls.DEFAULT_RETRIES}): {url}")

            try:
                with cls._get_session(verify, cert, timeout) as session:
                    # 在这里设置超时参数
                    response = session.post(url, data=data, json=json, headers=headers, timeout=timeout)
                    return cls._handle_response(response)
            except RequestException as e:
                last_exception = e
                # 只在明确指定重试异常且不是最后一次尝试时继续
                if not retry_on_exception or attempt >= cls.DEFAULT_RETRIES:
                    raise

        # 如果循环结束仍未成功，抛出最后一个异常
        raise last_exception